﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SyncList.Business.ValueObject
{


    /// <summary>
    /// Contenu d'un répertoire, et le répertoire parent
    /// </summary>
    public class DirectoryContent : ItemBase
    {

        #region Attributs

        /// <summary>
        /// Liste des dossiers enfant
        /// </summary>
        private Dictionary<string, DirectoryContent> _directories = new Dictionary<string,DirectoryContent>();

        /// <summary>
        /// Files of this Directory
        /// </summary>
        private List<FileItem> _files = new List<FileItem>();


        /// <summary>
        /// Chemin relatif au parent
        /// </summary>
        private string _relativePath;

        /// <summary>
        /// Chemin absolu
        /// </summary>
        private string _absolutePath;


        /// <summary>
        /// Type de répertoire
        /// </summary>
        private DirectoryTypeEnum _directoryType = DirectoryTypeEnum.Standard;


        /// <summary>
        /// Niveau
        /// </summary>
        private int _level = 0;

      


        #endregion

        #region Events

        /// <summary>
        /// Indiquer qu'il y a eu du changement
        /// </summary>
        public event EventHandler ItemsChanged;


        protected void OnItemsChanged()
        {
            if(ItemsChanged != null)
                ItemsChanged(this, new EventArgs());
        }

        #endregion

        #region Constructeur


        /// <summary>
        /// Create a new directory content
        /// </summary>
        /// <param name="parent">Parent directory</param>
        /// <param name="relativePath">Parent path</param>
        /// <param name="directoryType">Type de dossier</param>
        public DirectoryContent(DirectoryContent parent, string relativePath): base(parent)
        {
          
            _relativePath = relativePath;

            string absolutePath = relativePath;

            if (parent != null)
            {
                if (parent.DirectoryType == ValueObject.DirectoryTypeEnum.User)
                    DirectoryType = ValueObject.DirectoryTypeEnum.HardDisk;

                absolutePath = parent.AbsolutePath;
                if(!String.IsNullOrWhiteSpace(relativePath))
                    absolutePath += @"\" + relativePath;
            }
                         
            _absolutePath = absolutePath;

            if (parent != null)
                _level = parent.Level + 1;

        }

        #endregion

        #region Propriétés


        /// <summary>
        /// Niveau
        /// </summary>
        public int Level
        {
            get { return _level; }
        }


        /// <summary>
        /// Liste des items, directory puis fichier
        /// </summary>
        public IEnumerable<ItemBase> Items
        {
            get
            {
                foreach(DirectoryContent directory in this._directories.Values.OrderBy(d => d.Name))
                    yield return directory;

                foreach(FileItem file in this._files.OrderBy(d => d.Name))
                    yield return file;
            }
        }

        /// <summary>
        /// Type de répertoire
        /// </summary>
        public DirectoryTypeEnum DirectoryType
        {
            get { return _directoryType; }
            set { _directoryType = value; OnPropertyChanged("DirectoryType"); }
        }


        /// <summary>
        /// Liste des enfants
        /// </summary>
        public Dictionary<string, DirectoryContent> Directories
        {
            get { return (_directories); }
        }


        /// <summary>
        /// Files of this Directory
        /// </summary>
        public List<FileItem> Files
        {
            get { return (_files); }
        }


        /// <summary>
        /// Chemin relatif au parent
        /// </summary>
        public string RelativePath
        {
            get { return _relativePath; }
        }


        /// <summary>
        /// Chemin absolu
        /// </summary>
        public string AbsolutePath
        {
            get { return _absolutePath; }
            set { _absolutePath = value; OnPropertyChanged("AbsolutePath"); }
        }



        /// <summary>
        /// Taille des fichiers du dossier
        /// </summary>
        public override long Size
        {
            get 
            {                
                long total = 0;
                if (_directoryType != DirectoryTypeEnum.Back)
                {
                    foreach (ItemBase item in _directories.Values)
                        total += item.Size;
                    total += FilesSize;
                }
                return (total);            
            }
            set
            {
                throw new NotSupportedException("Impossible to set a folder size, it is computed from its content");
            }
        }


        /// <summary>
        /// Nombre de fichiers au total ici et dans tous les enfants
        /// </summary>
        public override int FileCount
        {
            get
            {
                int count = LocalFileCount;
                foreach (DirectoryContent directory in _directories.Values)
                    count += directory.FileCount;
                return (count);
            }
        }


        /// <summary>
        /// Nom du fichier / du dossier
        /// </summary>
        public override string Name
        {
            get { return (_relativePath); }
            set { _relativePath = value; }
        }

        /// <summary>
        /// Indique si c'est un dossier
        /// </summary>
        public override bool IsFolder
        {
            get { return (true); }
        }


        /// <summary>
        /// Nombre de fichiers à l'endroit courant
        /// </summary>
        private int LocalFileCount
        {
            get { return (_files.Count); }
        }



       

        /// <summary>
        /// Taille des fichiers du dossier
        /// </summary>
        private long FilesSize
        {
            get { return(_files.Sum(f => f.Size)); }
        }

        #endregion

        #region Accesseurs calculés



     

        /// <summary>
        /// Nombre de dossiers à l'endroit courant
        /// </summary>
        public int TotalDirectoryCount
        {
            get 
            {
                int count = _directories.Count;
                foreach (DirectoryContent directory in _directories.Values)
                    count += directory.TotalDirectoryCount;
                return (count);
            }
        }


        /// <summary>
        /// Parcourir tous les fichiers de façon récursive pour en extraire la liste complète
        /// </summary>
        public IEnumerable<FileItem> AllFiles
        {
            get
            {
                foreach (FileItem file in this._files)
                    yield return(file);

                foreach(DirectoryContent directory in this._directories.Values)
                {
                    foreach (FileItem item in directory.AllFiles)
                        yield return(item);
                }
            }
        }



        /// <summary>
        /// Liste de tous les répertoires
        /// </summary>
        public IEnumerable<DirectoryContent> AllDirectories
        {
            get
            {
                yield return (this);

                foreach (DirectoryContent directory in this._directories.Values)
                {
                    yield return (directory);

                    foreach (DirectoryContent item in directory.AllDirectories)
                        yield return(item);
                }
            }
        }



        /// <summary>
        /// Tooltip
        /// </summary>
        public override string ToolTip
        {
            get { return (Name + " : " + FileCount.ToString() + " files " + SizeCaption.ToString()); }
        }

        #endregion

        #region Méthodes


        /// <summary>
        /// Write a file at the right place
        /// </summary>
        /// <param name="file">Fichier à inscire</param>
        /// <param name="fullPath">Chemin complet restant (après appel récursif)</param>
        public DirectoryContent PlaceFileInTree(FileItem file, string fullPath)
        {
            if (!fullPath.EndsWith(@"\"))
                fullPath += @"\";

            DirectoryContent directory = GetDirectory(fullPath);
            if (directory != null)
                directory.AddFile(file);                
            return (directory);
        }


        /// <summary>
        /// Ajouter un fichier dans un dossier
        /// </summary>
        /// <param name="file"></param>
        public void AddFile(FileItem file)
        {
            if (file == null) throw new ArgumentNullException("file");
            //if (file.Parent != null) throw new NotSupportedException("Le fichier '" + file.Name + "' a déjà un parent");            
            //file.Parent = this;
            if (!this.Files.Contains(file))
            {
                this.Files.Add(file);
                OnItemsChanged();
            }
        }

        /// <summary>
        /// Supprimer un fichier de l'arbre
        /// </summary>
        /// <param name="item"></param>
        public DirectoryContent FindItemInTree(FileItem file)
        {            
            // - Le dossier en cours contient le fichier
            if (_files.Contains(file))
                return (this);

            DirectoryContent result = null;

            foreach(DirectoryContent directory in _directories.Values)
            {
                result = directory.FindItemInTree(file);
                if (result != null)
                    break;
            }
            return (result);
        }



        /// <summary>
        /// Enlever un fichier de l'arborescence
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public bool RemoveItemFromTree(FileItem file)
        {
            bool result = false;
            DirectoryContent directory = FindItemInTree(file);
            if (directory != null)
            {
                result = true;
                directory.RemoveItem(file);
            }
            return (result);
        }

      

        /// <summary>
        /// Enlever un fichier
        /// </summary>
        /// <param name="item"></param>
        private void RemoveItem(ItemBase item)
        {
            FileItem file = item as FileItem;
            if (file != null)
            {
                if (!_files.Contains(item)) throw new NotSupportedException("Impossible de supprimer le fichier du répertoire car il n'en fait pas partie");
                _files.Remove(file);
                OnItemsChanged();
            }

            DirectoryContent directory = item as DirectoryContent;
            if (directory != null)
            {
                if (!_directories.ContainsKey(directory.Name)) throw new NotSupportedException("Impossible de supprimer le répertoire du répertoire car il n'en fait pas partie");
                _directories.Remove(directory.Name);
                OnItemsChanged();
            }

            // - Si le dossier est vide je le supprime
            if ((_files.Count == 0)&&(this.Parent != null))
                this.Parent.RemoveItem(this);
        }


        /// <summary>
        /// Trouver le dossier pour un chemin complet
        /// </summary>
        /// <param name="fullPath">Chemin complet à trouver</param>
        /// <returns></returns>
        public DirectoryContent GetDirectory(string fullPath)
        {
            if (String.IsNullOrWhiteSpace(fullPath))
                return (this);
            else
            {
                string folderName = "";
                string subFolders = "";

                int pos = fullPath.IndexOf("\\");
                if (pos >= 0)
                {
                    folderName = fullPath.Substring(0, pos);
                    subFolders = "";

                    if (pos + 1 < fullPath.Length)
                        subFolders = fullPath.Substring(pos + 1);
                }
                else
                {
                    folderName = fullPath;
                    subFolders = "";
                }

              

                DirectoryContent child = null;

                if (!_directories.TryGetValue(folderName, out child))
                {
                    child = new DirectoryContent(this, folderName);
                    AddDirectory(folderName, child);                    
                }

                // - Recursive call to write file at the right place
                return (child.GetDirectory(subFolders));

            }
        }


        /// <summary>
        /// Ajouter un répertoire
        /// </summary>
        /// <param name="directory"></param>
        private void AddDirectory(string folderName, DirectoryContent directory)
        {
            Directories.Add(folderName, directory);
            OnItemsChanged();
        }


        /// <summary>
        /// Renvoyer tout le chemin en parties
        /// </summary>
        /// <returns></returns>
        public DirectoryContent[] GetPathParts()
        {
            List<DirectoryContent> directories = new List<DirectoryContent>();
            if (Parent != null)
                directories.AddRange(Parent.GetPathParts());
            directories.Add(this);
            return (directories.ToArray());
        }


        #region Méthodes

        public override string ToString()
        {
            return (base.ToString() + " F:" + _files.Count.ToString() +  " D:" + _directories.Count.ToString());
        }

     
        #endregion


        #endregion


        /// <summary>
        /// Vider
        /// </summary>
        public void Clear()
        {
            _directories.Clear();
            _files.Clear();
        }









    }
}
